﻿using CdioCs;
using Gimela.Net.Sockets;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading;
using System.Windows.Forms;

namespace UniRlv.VisionScapeAdapter
{
    class Program
    {
        //高精度定时
        static MillisecondTimer g_IDTimer;
        static UInt32 nOldDI = 0;
        static int loops = 0;
        static int nTrigTimes = 0;
        static int nLowPeriod = 0;
        static int nHighPeriod = 0;
        
        static int NO_TRIG=0;
        static int ON_TRIGING=1;
        static int FINISH_TRIGING=2;
        static int FINISH_SENDING=3;
        static int ON_EDGING=4;

        //配置文件信息
        static int g_nPin;
        static int g_nTrigTotal;
        static string g_sErr;
        static int g_nDecodeNum;
        static int g_nCaseDelim;
        static int g_nTrigDelim;//触发间隔
        static int g_nTrigDelay;//上升沿触发延时
        static string g_sTrig;
        static int PERIOD_CYCLE = 10;//10ms定时间隔
        static int g_nTrigState = NO_TRIG;//0:无触发 1：触发中 2：触发完毕 3：数据发送完毕
        static List<string> g_saValidCodes = new List<string>();
        static string recData = string.Empty;

        static string SEC_SCANNER="采集器";
        static string SEC_LOCAL="本地";
        static string SEC_BOARD="控制卡";
        static string SEC_OTHER="其他";
        static string KEY_BOARD_ADDR="卡地址";
        static string KEY_PIN_TRIG="触发地址";
        static string KEY_IP="IP地址";
        static string KEY_PORT="端口";
        static string KEY_PORT_REC="接收端口";
        static string KEY_PORT_TRIG="触发端口";
        static string KEY_DECODE_ERROR="解码错误";
        static string KEY_DECODE_CNT="码数量";
        static string KEY_TRIG_CNT="触发次数";
        static string KEY_TRIG_DATA="触发数据";
        static string KEY_CASE_DELIM="盒间隔";
        static string KEY_TRIG_DELIM="触发间隔";
        static string KEY_TRIG_DELAY="触发延时";

        static AsyncTcpClient cliTrig;
        static AsyncTcpClient cliRec;
        static AsyncTcpServer srv;

        static Cdio g_io;
        static short m_Id;
        static long g_ticks = 0;
        static bool bDetectTrig = false, bDetectRec = false;

        static void Main(string[] args)
        {
            int processCount = 0;
            Process[] pa = Process.GetProcesses();//获取当前进程数组。  
            foreach (Process PTest in pa)
            {
                if (PTest.ProcessName == Process.GetCurrentProcess().ProcessName)
                {
                    processCount += 1;
                }

            }
            if (processCount > 1)
            {
                return;
            }

            AppDomain.CurrentDomain.UnhandledException += CurrentDomain_UnhandledException;

            //测试
            //int edg = EdgeDetect(256, 0, 8);

            #region 其他初始化

            g_nPin = IniFileRW.ReadInt(SEC_BOARD, KEY_PIN_TRIG, 8);
            g_nTrigTotal = IniFileRW.ReadInt(SEC_OTHER, KEY_TRIG_CNT, 8);
		    g_sErr = IniFileRW.ReadString(SEC_OTHER, KEY_DECODE_ERROR, "<error>");
            g_nDecodeNum = IniFileRW.ReadInt(SEC_OTHER, KEY_DECODE_CNT, 10);
            g_nCaseDelim = IniFileRW.ReadInt(SEC_OTHER, KEY_CASE_DELIM, 300);
            g_nTrigDelim = IniFileRW.ReadInt(SEC_OTHER, KEY_TRIG_DELIM, 200); 
		    g_sTrig = IniFileRW.ReadString(SEC_OTHER, KEY_TRIG_DATA, "SCAN");
            g_nTrigDelay = IniFileRW.ReadInt(SEC_OTHER, KEY_TRIG_DELAY, 100);

            Trace.WriteLine(string.Format("\n配置信息：pin={0}, trig={1}, err={2}, decode={3}, case delim={4}, trig delim={5}, trig string={6}, trig delay={7}\n", 
			g_nPin, g_nTrigTotal, g_sErr, g_nDecodeNum, g_nCaseDelim, g_nTrigDelim, g_sTrig, g_nTrigDelay));
            #endregion
            
            #region 启动IO卡和监听定时器
            string sTmp = IniFileRW.ReadString(SEC_BOARD, KEY_BOARD_ADDR, "DIO000");

            int Ret;
            g_io = new Cdio();
            Ret = g_io.Init(sTmp, out m_Id);

            if (Ret != 0)
            {
                string ErrorString;
                g_io.GetErrorString(Ret, out ErrorString);
                MessageBox.Show("IO卡初始化失败:" + ErrorString, "出现错误", MessageBoxButtons.OK, MessageBoxIcon.Error, MessageBoxDefaultButton.Button1);
                return;
            }

            g_IDTimer = new MillisecondTimer();
            g_IDTimer.Interval = PERIOD_CYCLE;
            g_IDTimer.Tick += g_IDTimer_Tick;
            g_IDTimer.Start();
        #endregion

            InitRecSocket();
            InitTrigSocket();

            string sIP = IniFileRW.ReadString(SEC_LOCAL, KEY_IP, "127, 0, 0, 1");
            int port = IniFileRW.ReadInt(SEC_LOCAL, KEY_PORT, 14920);
            srv = new AsyncTcpServer(IPAddress.Parse(sIP), port);
            srv.ClientConnected += srv_ClientConnected;
            srv.Encoding = Encoding.UTF8;
            srv.Start();

            cliRec.Connect();
            cliTrig.Connect();

            while (true)
            {

                if (!string.IsNullOrEmpty(recData))//是否加上正在处于触发状态？？  && g_nTrigState==ON_TRIGING
			    {
				    string[] saCodes = recData.Split(new char[]{','});
                    Console.WriteLine("Get Data:{0}, 数量={1}\n", recData, saCodes.Length);
                    recData = string.Empty;
				    //处理数据，比对
                    foreach (var item in saCodes)
	                {
		                if (IsValidCode(item))
					    {
						    if (!g_saValidCodes.Contains(item))
						    {//未找到相同码，并且合法
							    g_saValidCodes.Add(item);
						    }
					    }
	                }

			    }

			    if (g_nTrigState==FINISH_TRIGING)
			    {
				    string sSend = GenCodeString(g_saValidCodes);
				    //发送字符串
				    g_nTrigState = FINISH_SENDING;
				    Console.WriteLine("Send {0}\n", sSend);
				    g_saValidCodes.Clear();

                    if (srv.IsRunning)
                    {
                        srv.SendToAll(sSend);
                    }
			    }

                //if (Console.Read()>0)
                //{
                //    try
                //    {
                //        cliRec.Send("test send");
                //        Console.WriteLine("test send");
                //    }
                //    catch (Exception exp)
                //    {
                //        Console.WriteLine("test send: " + exp.Message);
                //    }
                //}
			    Thread.Sleep(10);
            }
        }

        static void InitRecSocket()
        {
            string sIP = IniFileRW.ReadString(SEC_SCANNER, KEY_IP, "127, 0, 0, 1");
            int port = IniFileRW.ReadInt(SEC_SCANNER, KEY_PORT_REC, 14911);

            IPEndPoint remoteEP = new IPEndPoint(IPAddress.Parse(sIP), port);
            cliRec = new AsyncTcpClient(remoteEP);
            cliRec.Encoding = Encoding.UTF8;
            cliRec.PlaintextReceived += cliRec_PlaintextReceived;
            cliRec.ServerConnected += cliRec_ServerConnected;
            cliRec.ServerDisconnected += cliRec_ServerDisconnected;
            cliRec.ServerExceptionOccurred += cliRec_ServerExceptionOccurred;
            cliRec.RetryInterval = 5;
            cliRec.Retries = 10000;
        }

        static void InitTrigSocket()
        {
            string sIP = IniFileRW.ReadString(SEC_SCANNER, KEY_IP, "127, 0, 0, 1");
            int port = IniFileRW.ReadInt(SEC_SCANNER, KEY_PORT_TRIG, 14912);
            IPEndPoint remoteEP = new IPEndPoint(IPAddress.Parse(sIP), port);

            cliTrig = new AsyncTcpClient(remoteEP);
            cliTrig.Encoding = Encoding.UTF8;
            cliTrig.ServerConnected += cliTrig_ServerConnected;
            cliTrig.ServerDisconnected += cliTrig_ServerDisconnected;
            cliTrig.ServerExceptionOccurred += cliTrig_ServerExceptionOccurred;
            cliTrig.RetryInterval = 5;
            cliTrig.Retries = 10000;
        }

        private static void cliTrig_ServerExceptionOccurred(object sender, TcpServerExceptionOccurredEventArgs e)
        {
            Console.WriteLine("trig disconnect(srv exception):" + e.Exception.Message);
            cliTrig.Close();

            InitTrigSocket();
            cliTrig.Connect();
        }

        private static void cliTrig_ServerDisconnected(object sender, TcpServerDisconnectedEventArgs e)
        {
            Console.WriteLine("trig disconnect:" + e.ToString());
            cliTrig.Close();

            InitTrigSocket();
            cliTrig.Connect();
            bDetectTrig = false;
        }

        static void cliRec_ServerExceptionOccurred(object sender, TcpServerExceptionOccurredEventArgs e)
        {
            Console.WriteLine("rec disconnect(srv exception):" + e.Exception.Message);
            cliRec.Close();

            InitRecSocket();
            cliRec.Connect();
            bDetectRec = false;
        }

        private static void cliRec_ServerDisconnected(object sender, TcpServerDisconnectedEventArgs e)
        {
            Console.WriteLine("rec disconnect:" + e.ToString());
            cliRec.Close();

            InitRecSocket();
            cliRec.Connect();
        }

        static void srv_ClientConnected(object sender, TcpClientConnectedEventArgs e)
        {
            Console.WriteLine("get a client:" + e.ToString());
        }

        static void cliTrig_ServerConnected(object sender, TcpServerConnectedEventArgs e)
        {
            Console.WriteLine("trig connect:" + e.ToString());
            bDetectTrig = true;
        }

        static void cliRec_ServerConnected(object sender, TcpServerConnectedEventArgs e)
        {
            Console.WriteLine("rec connect:" + e.ToString());
            bDetectRec = true;
        }

        static void g_IDTimer_Tick(object sender, EventArgs e)
        {
            byte bDITmp;
            UInt32 nDI = 0xAA;
            int nEdge;

            g_io.InpByte(m_Id, 1, out bDITmp);
            nDI = bDITmp;
            nDI <<= 8;
            g_io.InpByte(m_Id, 0, out bDITmp);
            nDI += bDITmp;


            nEdge = EdgeDetect(nDI, nOldDI, g_nPin);
            if (nEdge == 1)
            {//上升沿
                if (g_nTrigState == NO_TRIG)
                {
                    g_nTrigState = ON_EDGING;
                    loops = 0;
                    Console.WriteLine("ON_EDGING\n");
                }
                nLowPeriod = 0;
                nHighPeriod = 0;

            }
            else if (nEdge == 2)
            {//下降沿
            }
            else if (nEdge == -1)//high
            {
                nHighPeriod++;
                if (g_nTrigState == ON_EDGING && nHighPeriod * PERIOD_CYCLE >= g_nTrigDelay)
                {
                    g_nTrigState = ON_TRIGING;
                    Console.WriteLine("ON_TRIGING, nHighPeriod={0}\n", nHighPeriod);
                }
            }
            else//low
            {
                nLowPeriod++;
                if (g_nTrigState == FINISH_SENDING && nLowPeriod * PERIOD_CYCLE >= g_nCaseDelim)
                {
                    g_nTrigState = NO_TRIG;
                    Console.WriteLine("NO_TRIG\n");
                }
            }


            loops++;

            if (loops * PERIOD_CYCLE >= g_nTrigDelim && g_nTrigState == ON_TRIGING)//g_nTrigDelim毫秒触发一次
            {
                if (cliTrig.Connected)
                {
                    if (nTrigTimes < g_nTrigTotal)
                    {
                        if (g_saValidCodes.Count != g_nDecodeNum)
                        {//端口打开，并且解码数量仍然不够
                            cliTrig.Send(g_sTrig);
                            Console.WriteLine("trig {0}\n", nTrigTimes);
                        }
                        else
                        {
                            Console.WriteLine("trig No need or no open {0}\n", nTrigTimes);
                        }

                        nTrigTimes++;
                    }
                    else
                    {//触发次数已够
                        g_nTrigState = FINISH_TRIGING;
                        nTrigTimes = 0;
                    }


                }

                loops = 0;
            }

            nOldDI = nDI;

            g_ticks++;

            if (g_ticks%5000==0)
            {//5秒一次检测

                if (!cliTrig.Connected && bDetectTrig)
                {
                    Console.WriteLine("cliTrig found DisConnected, reconnect!");           
                    cliTrig.Close();

                    InitTrigSocket();
                    cliTrig.Connect();
                    bDetectTrig = false;
                }

                if (!cliRec.Connected && bDetectRec)
                {
                    Console.WriteLine("cliRec found DisConnected, reconnect!");  
                    cliRec.Close();

                    InitRecSocket();
                    cliRec.Connect();
                    bDetectRec = false;
                }
            }
        }

        static void cliRec_PlaintextReceived(object sender, TcpDatagramReceivedEventArgs<string> e)
        {
            recData = e.Datagram;
        }

        private static void CurrentDomain_UnhandledException(object sender, UnhandledExceptionEventArgs e)
        {
            Trace.WriteLine("未捕获异常：" + e.ExceptionObject.ToString());
        }

        #region 辅助函数
        
        static int EdgeDetect( UInt32 nDIVal, UInt32 nOldVal, int nBit )
        {
            UInt32 tmp = (UInt32)(1<<nBit);
            UInt32 nNewLevel = nDIVal & tmp;
            UInt32 nOldLevel = nOldVal & tmp;

	        if (nOldLevel!=nNewLevel)
	        {

		        if (nOldLevel==0)
		        {//上升沿
			        return 1;
		        } 
		        else
		        {//下降沿
			        return 2;
		        }
	        }
	        else
	        {
		        if(nNewLevel==0)
			        return 0;//no change:low
		        else
			        return -1;//no change:high
	        }

        }

        static bool IsValidCode( string strCode )
        {
            return IsNumeric(strCode);
        }

        public static bool IsNumeric(string value)
        {
            return Regex.IsMatch(value, @"^[+-]?\d*[.]?\d*$");
        }

        public static string GenCodeString(IList<string> sa)
        {
            string strRet = "";

	        if (sa.Count==0)
	        {
		        return g_sErr;
	        }
	        else
	        {
                strRet = sa[0];
	        }

	        for (int i=1; i<g_nDecodeNum; i++)
	        {
                strRet += ",";

		        if (sa.Count>i)
		        {
                    strRet += sa[i];
		        } 
		        else
		        {
                    strRet += g_sErr;
		        }
	        }

            return strRet;
        }

        #endregion
    }
}
